/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/
package nfse.alert;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.*;
import java.sql.*;

import nfse.NetFSE;
import nfse.NetFSERecord;
import nfse.NetFSEUtil;
import nfse.SensorInfo;

/*******************************************************************************
 * This class receives alert records (typically from a DataSocket) and inserts
 * them into a normalized alert database. This aspect of Net/FSE is very new and
 * not fully implemented/realized.
 * 
 * @author bduphoff
 * 
 */
public class AlertSocket extends Thread {

    /***************************************************************************
     * The AlertServer that spawned this thread. Used to call methods on the
     * server.
     */
    private AlertServer alertServer;

    /***************************************************************************
     * The socket connection associated with this thread.
     */
    private Socket sock;

    /***************************************************************************
     * Basic constructor sets the various attributes needed to process alerts.
     * 
     * @param s
     *            The socket to communicate with the connected client over
     * @param alertServer
     *            The nfse.alert.AlertServer that created this instance
     */
    public AlertSocket(Socket s, AlertServer alertServer) {
        sock = s;
        this.alertServer = alertServer;
    }

    /***************************************************************************
     * Reads the header (sensor ID) and starts reading records one at a time.
     * Each record is sent by the client preceeded by the length in bytes, which
     * tells the AlertSocket how many bytes to read.
     */
    public void run() {
        try {

            Statement statement = alertServer.createStatement();

            long startMS = System.currentTimeMillis();
            DataInputStream dis = new DataInputStream(sock.getInputStream());
            DataOutputStream dos = new DataOutputStream(sock.getOutputStream());
            int sensorID = dis.readInt(); // read the sensor ID

            SensorInfo sensorInfo = NetFSE.getSensorInfo(sensorID);

            int typeID = sensorInfo.getToolID();

            NetFSERecord rec = ((NetFSERecord) NetFSE.TYPE_CLASSES.get(new Integer(sensorInfo.getToolID())));

            // Unix time in MS used to monitor throughput
            long ts = System.currentTimeMillis();
            int len = 0;
            long count = 0;
            try {
                while (true) {
                    try {
                        len = dis.readInt();
                    } catch (Exception e) {
                        len = -1;
                    }
                    if (len == -1) {
                        break;
                    }

                    // Record the time at which we got the alert
                    long detectionTS = System.currentTimeMillis();

                    int bytesRead = 0;
                    byte[] dataBytes = new byte[len];
                    while (bytesRead < len)
                        bytesRead += dis.read(dataBytes, bytesRead, len - bytesRead);

                    if (bytesRead != len) {
                        System.err.println("Bad data, not enough bytes.");
                    } else {
                        NetFSERecord record = null;
                        try {
                            record = rec.generate(new String(dataBytes), sensorInfo.getSensorID());
                            record.setTypeID(typeID);
                        } catch (Exception e) {
                            // System.out.println(new
                            // String(dataBytes));
                            // e.printStackTrace();
                            record = null;
                        }

                        /*******************************************************
                         * Only process non-null records that are alerts.
                         */
                        if (record != null && record.isAlert()) {

                            // Add the record to the parent AlertServer
                            this.alertServer.addRecord(statement, detectionTS, record);

                            count++;
                            if (count % 10000 == 0) {
                                // Output statistics periodically
                                long ts2 = System.currentTimeMillis();
                                long ms = ts2 - ts;
                                long elapsedMS = ts2 - startMS;
                                float elapsedSec = (float) elapsedMS / (float) 1000;
                                float sec = (float) ms / (float) 1000;
                                float sps1 = (float) count / elapsedSec;
                                float sps2 = (float) 10000 / sec;
                                System.out.println("AlertSocket: Total Records=" + NetFSEUtil.formattedInteger(count)

                                + "; Current RPS= " + NetFSEUtil.formattedDouble(sps2) + "; Average RPS= "
                                        + NetFSEUtil.formattedDouble(sps1));
                                ts = System.currentTimeMillis();
                            }
                        }
                    }
                }

            } catch (Exception E) {
                E.printStackTrace();
            }
            dis.close();
            dos.close();
            sock.close();
            statement.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}